#include "cache.h"
#include "common.h"
#include "burst.h"
#include <time.h>
#include <stdlib.h>

void ddr3_read_me(hwaddr_t addr, void* data);
void ddr3_write_me(hwaddr_t addr, void* data, uint8_t* mask);
void dram_write(hwaddr_t addr, size_t len, uint32_t data);

//初始化cache
void init_cache() {
    int i;
    for (i = 0; i < CACHE_L1_S; i++) {
        int j;
        for (j = 0; j < CACHE_L1_E; j++) {
            cache_L1[i].cache_L1_blocks[j].val = false;
        }
    }
    return;
}

//读cache
int read_cache_L1(hwaddr_t addr) {
    uint32_t set = ((addr >> CACHE_b) & (CACHE_L1_S - 1));  //组索引
    uint32_t tag = (addr >> (CACHE_b + CACHE_L1_s));        //tag
    uint32_t addr_start = ((addr >> CACHE_b) << CACHE_b);   //若需要替换时应从该地址开始替换

    int block_i;                                                                    //!!!
    for (block_i = 0; block_i < CACHE_L1_E; block_i++) {
        if (cache_L1[set].cache_L1_blocks[block_i].val && cache_L1[set].cache_L1_blocks[block_i].tag == tag) { //命中
            return block_i;
        }
    }

    //若未命中
    srand(time(0));

    //查找有无空块
    for (block_i = 0; block_i < CACHE_L1_E; block_i++) {
        if (!cache_L1[set].cache_L1_blocks[block_i].val) {
            break;
        }
    }

    //无空块，随机替换
    if (block_i == CACHE_L1_E) {
        block_i = rand() % CACHE_L1_E;
    }

    int i;
    for (i = 0; i < CACHE_B / BURST_LEN; i++) {
    ddr3_read_me(addr_start + BURST_LEN * i, cache_L1[set].cache_L1_blocks[block_i].data + BURST_LEN * i);
    }

    cache_L1[set].cache_L1_blocks[block_i].val = true;
    cache_L1[set].cache_L1_blocks[block_i].tag = tag;    

    return block_i;
}

//写操作
int write_cache_L1(hwaddr_t addr, size_t len, uint32_t data) {
    uint32_t set = ((addr >> CACHE_b) & (CACHE_L1_S - 1));
    uint32_t tag = (addr >> (CACHE_b + CACHE_L1_s));
    uint32_t block_bias = addr & (CACHE_B - 1);

    int block_i;
    for (block_i = 0; block_i < CACHE_L1_E; block_i++) {
        if (cache_L1[set].cache_L1_blocks[block_i].val && cache_L1[set].cache_L1_blocks[block_i].tag == tag) {  //命中

            // 假如数据超出当前缓存块，需要分隔存储
            if (block_bias + len > CACHE_B) {
                dram_write(addr, CACHE_B - block_bias, data);
                memcpy(cache_L1[set].cache_L1_blocks[block_i].data + block_bias, &data, CACHE_B - block_bias);
                write_cache_L1(addr + CACHE_B - block_bias, len - (CACHE_B - block_bias), data >> (CACHE_B - block_bias));
            } 
            else {
                dram_write(addr, len, data);
                memcpy(cache_L1[set].cache_L1_blocks[block_i].data + block_bias, &data, len);
            }
            return 1;
        }
    }

    //未命中
    return 0;
}



